package com.jc.common.util;

import java.util.Date;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.druid.support.spring.stat.SpringStatUtils;

public class IdGenerator {

	
	
	/**
	 * SnowFlake算法 64位Long类型生成唯一ID 第一位0，表明正数 2-42，41位，表示毫秒时间戳差值，起始值自定义
	 * 43-52，10位，机器编号，5位数据中心编号，5位进程编号 53-64，12位，毫秒内计数器 本机内存生成，性能高
	  * 主要就是三部分： 时间戳，进程id，序列号 时间戳41，id10位，序列号12位

	 */
	private final static long beginTs = 1483200000000L;
	private long lastTs = 0L;
	private long processId;
	private int processIdBits = 10;
	private long sequence = 0L;
	private int sequenceBits = 12;
 
	public IdGenerator() {
		long processId = new Double(Math.random() * 1023).longValue();
		if (processId > ((1 << processIdBits) - 1)) {
			throw new RuntimeException("进程ID超出范围，设置位数" + processIdBits + "，最大"
					+ ((1 << processIdBits) - 1));
		}
		this.processId = processId;
	}
	// 10位进程ID标识
	public IdGenerator(long processId) {
		if (processId > ((1 << processIdBits) - 1)) {
			throw new RuntimeException("进程ID超出范围，设置位数" + processIdBits + "，最大"
					+ ((1 << processIdBits) - 1));
		}
		this.processId = processId;
	}
 
	protected long timeGen() {
		return System.currentTimeMillis();
	}
 
	public synchronized long nextId() {
		long ts = timeGen();
		if (ts < lastTs) {// 刚刚生成的时间戳比上次的时间戳还小，出错
			throw new RuntimeException("时间戳顺序错误");
		}
		if (ts == lastTs) {// 刚刚生成的时间戳跟上次的时间戳一样，则需要生成一个sequence序列号
			sequence = (sequence + 1) & ((1 << sequenceBits) - 1);  // sequence循环自增
			// 如果sequence=0则需要重新生成时间戳
			if (sequence == 0) {
				// 且必须保证时间戳序列往后
				ts = nextTs(lastTs);
			}
		} else {// 如果ts>lastTs，时间戳序列已经不同了，此时可以不必生成sequence了，直接取0
			sequence = 0L;
		}
		lastTs = ts;// 更新lastTs时间戳
		return ((ts - beginTs) << (processIdBits + sequenceBits)) | (processId << sequenceBits)	| sequence;
	}
	
	
	
	
	/***
	 * 
	 * @param type 前后缀类型 0 前缀 1 后缀
	 * @param fillContext : 前后缀内容
	 * @return
	 */
	public synchronized String nextId(int type,String fillContext) {
		long ts = timeGen();
		if (ts < lastTs) {// 刚刚生成的时间戳比上次的时间戳还小，出错
			throw new RuntimeException("时间戳顺序错误");
		}
		if (ts == lastTs) {// 刚刚生成的时间戳跟上次的时间戳一样，则需要生成一个sequence序列号
			sequence = (sequence + 1) & ((1 << sequenceBits) - 1);  // sequence循环自增
			// 如果sequence=0则需要重新生成时间戳
			if (sequence == 0) {
				// 且必须保证时间戳序列往后
				ts = nextTs(lastTs);
			}
		} else {// 如果ts>lastTs，时间戳序列已经不同了，此时可以不必生成sequence了，直接取0
			sequence = 0L;
		}
		lastTs = ts;// 更新lastTs时间戳
		long ret = ((ts - beginTs) << (processIdBits + sequenceBits)) | (processId << sequenceBits)
				| sequence;
		
		if(StringUtils.isBlank(fillContext)) fillContext = "";
		if(type == 0) {//前缀
			return fillContext + ret;
		}else {//后缀
			return ret + fillContext;
		}
	}
	
	
	protected long nextTs(long lastTs) {
		long ts = timeGen();
		while (ts <= lastTs) {
			ts = timeGen();
		}
		return ts;
	}
 
	
	/***
	 *   获取有意义的唯一ID 如果是分布式系统，并发极高的情况下，可以配置一下系统环境变量 : key : group_server_id  ,值 1--9中的任意数字，集群中不重复.
	 * @param 19位，每4秒产生99万个不重复KEY，
	 * @return
	 */
	public static long getGeneratorId() {
		int group_server_id = 0;
		if(StringUtils.isNotBlank(System.getenv("group_server_id"))) {
			group_server_id = Integer.parseInt(System.getenv("group_server_id"));
		}
		long teimstap = Long.valueOf(DateTimeUtil.getDate(new Date(),"yyMMddHHmmss"));
		String val = teimstap + "" + group_server_id + "" + MemoryNumber.getKey();
		return Long.valueOf(val);
	}
	
	
	
	
	
	
	
	
	
	public static void main(String[] args) throws Exception {
		long num = new Double(Math.random() * 1023).longValue();
		IdGenerator ig = new IdGenerator();
		long begin = System.nanoTime();
		for (int i = 0; i < 1; i++) {
			
			//System.out.println(num + "====" + ig.nextId(0,"AA"));
			
			
			
			//System.out.println(num + "====" + teimstap +  ig.nextId() );
			
			
			//wrtieFile("d://12.txt",String.valueOf(ig.nextId()));
			
		}
		
		//getGeneratorId(11l);
		
		
		long ddssd = 1999999999999999999L;
		
		
		Thread t1 = new Thread(new MyThread());
        t1.start(); 
        
//        
        Thread t2 = new Thread(new MyThread());
        t2.start(); 
//        
//        
        Thread t3 = new Thread(new MyThread());
        t3.start(); 
//        
//        
        Thread t4 = new Thread(new MyThread());
        t4.start(); 
//        
        Thread t5 = new Thread(new MyThread());
        t5.start(); 

	}

}


class MyThread implements Runnable{
    @Override
    public void run() {
    	for(int i = 0 ; i < 100000 ; i++) {
//    		long teimstap = Long.valueOf(DateTimeUtil.getDate(new Date(),"yyMMddHHmmss"));
//    		String ds = teimstap + "" + MemoryNumber.getKey();
//    		System.out.println( ds );
    		
    		long ds = IdGenerator.getGeneratorId();
    		System.out.println( ds );
    		FileUtil.wrtieFile("d://33333333.txt", ds+"");
    	}
        
    }   
}
